package pl.edu.agh.uddiProxy.dao.hibernate;

import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityNotFoundException;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import pl.edu.agh.uddiProxy.dao.DAOHelper;
import pl.edu.agh.uddiProxy.dao.DynamicParameterDAO;
import pl.edu.agh.uddiProxy.query.DistanceFunction;
import pl.edu.agh.uddiProxy.types.ParameterType;
import pl.edu.agh.uddiProxy.types.ServiceDynamicParameters;

public class DynamicParameterHibernateDAO extends HibernateDaoSupport implements DynamicParameterDAO {
	private static final String QUERY_PREFIX = "SELECT s.tModelKey, s.id FROM serviceDynamicParameters s";
	private static final String QUERY_TMODEL_PREFIX = "SELECT s.tModelKey FROM serviceDynamicParameters s";
	private static final String QUERY_SUFIX = " GROUP BY s.tModelKey, s.id";
	
/*	SELECT * FROM serviceDynamicParameters s 
	JOIN serviceDynamicParameters_map sm1 ON s.id = sm1.serviceDynamicParameters_id LEFT JOIN parameterValue p1 ON p1.id = sm1.map_id and p1.id
	JOIN serviceDynamicParameters_map sm2 ON s.id = sm2.serviceDynamicParameters_id LEFT JOIN parameterValue p2 ON p2.id = sm2.map_id and p2.id
	JOIN serviceDynamicParameters_map sm3 ON s.id = sm3.serviceDynamicParameters_id LEFT JOIN parameterValue p3 ON p3.id = sm3.map_id and p3.id

	WHERE p2.name = "name1" and p2.value = 13 and p1.name = "name1" group by tModelKey*/
	
	private static String queryJoins;
	private static Logger logger = Logger.getLogger(DynamicParameterHibernateDAO.class);
	
	public String getQueryJoins() {
		if (queryJoins == null) {
			int size = DAOHelper.getParametersMap().getParametersMap().size();
			StringBuffer buffer = new StringBuffer();
			for (int i = 1; i <= size; i++) {
				buffer.append(" JOIN serviceDynamicParameters_map sm");
				buffer.append(i);
				buffer.append(" ON s.id = sm");
				buffer.append(i);
				buffer.append(".param_id LEFT JOIN parameterValue p");
				buffer.append(i);
				buffer.append(" ON p");
				buffer.append(i);
				buffer.append(".id = sm");
				buffer.append(i);
				buffer.append(".map_id and p");
				buffer.append(i);
				buffer.append(".id ");
			}
			buffer.append(" WHERE ");
			queryJoins = buffer.toString();
		}
		return queryJoins;
	}

	public void create(ServiceDynamicParameters dynamicParameter) throws DataAccessException {
		getHibernateTemplate().save(dynamicParameter);
		getHibernateTemplate().flush();
	}

	public void delete(ServiceDynamicParameters dynamicParameter) {
		getHibernateTemplate().delete(dynamicParameter);
		getHibernateTemplate().flush();
	}

	public boolean exists(String tModelKey) {
		return getByTModel(tModelKey) != null;
	}

	public List<ServiceDynamicParameters> getAll() {
		List<ServiceDynamicParameters> result = (List<ServiceDynamicParameters>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(final Session session)
							throws SQLException {
						Criteria criteria = session
								.createCriteria(ServiceDynamicParameters.class);
						return criteria.list();
					}
				});
		return result;
	}

	public List<Long> getAllIds() {
		List<ServiceDynamicParameters> entityList = getAll();
		List<Long> idList = new LinkedList<Long>();

		if (entityList == null) {
			return null;
		}

		for (ServiceDynamicParameters e : entityList) {
			idList.add(Long.parseLong(e.getTModelKey()));
		}

		return idList;
	}

	public void update(ServiceDynamicParameters dynamicParameter) {
		getHibernateTemplate().update(dynamicParameter);
		getHibernateTemplate().flush();
	}

	public boolean exists(Long id) {
		return getById(id) != null;
	}

	public ServiceDynamicParameters getById(final Long id) {
		if (id == null) {
			throw new EntityNotFoundException("ID is null");
		}

		ServiceDynamicParameters result = (ServiceDynamicParameters) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(final Session session)
					throws SQLException {
				Criteria criteria = session.createCriteria(ServiceDynamicParameters.class);
				criteria.add(Restrictions.eq("id", id));
				return criteria.uniqueResult();
			}
		});


		return result;
	}

	public ServiceDynamicParameters getByTModel(final String tModelKey) {
		if (tModelKey == null) {
			throw new EntityNotFoundException("ID is null");
		}

		ServiceDynamicParameters result = (ServiceDynamicParameters) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(final Session session)
					throws SQLException {
				Criteria criteria = session.createCriteria(ServiceDynamicParameters.class);
				criteria.add(Restrictions.eq("tModelKey", tModelKey));
				return criteria.uniqueResult();
			}
		});


		return result;
	}

	public List<String> getTModels(final String query) {
		return getTModels(query, null);
		
	}

	public List<String> getTModels(String query,
			List<String> models) {
		List<ServiceDynamicParameters> l = getDynamicParameters(query, models);
		List<String> list = new LinkedList<String>();
		
		for (ServiceDynamicParameters dynamicParameters : l) {
			list.add(dynamicParameters.getTModelKey());
		}
		
		return list;
	}

	public List<ServiceDynamicParameters> getDynamicParameters(String query) {
		return getDynamicParameters(query, null);
	}

	public List<ServiceDynamicParameters> getDynamicParameters(String query,
			List<String> models) {
		if (query == null) {
			return null;
		}

		final StringBuffer buffer = new StringBuffer();
		buffer.append(QUERY_PREFIX);
		buffer.append(getQueryJoins());
		buffer.append(transformQuery(query));
		if (models != null && models.size() > 0) {
			for (String tModel : models) { 
				buffer.append(" AND s.tModelKey = " + tModel);
			}
		}
		buffer.append(QUERY_SUFIX);
		
		logger.info(buffer.toString());
		
		List<ServiceDynamicParameters> list = (List<ServiceDynamicParameters>) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(final Session session)
					throws SQLException {
				SQLQuery sqlQuery = session.createSQLQuery(buffer.toString());
				sqlQuery.addEntity(ServiceDynamicParameters.class);
				return sqlQuery.list();
			}
		});

		return sort(list, query);
	}
	
	public String transformQuery(String query) {
		Map<String, ParameterType> map = DAOHelper.getParametersMap().getParametersMap();
		int i = 1;
		for (String name : map.keySet()) {
			query = query.replaceAll(name, "p" + i + ".name = '"+ name +"' and p" + i + ".value");
			i++;
		}
		
		return query;
	}
	
	
	private List<ServiceDynamicParameters> sort(List<ServiceDynamicParameters> list, String query) {
		List<Long> l = new LinkedList<Long>();
		
		for (ServiceDynamicParameters parameters : list) {
			l.add(new DistanceFunction(query, parameters).parse());
		}
		
		for (int i = 0; i < l.size(); i++) {
			long max = l.get(i);
			int k = i;
			for (int j = i + 1; j < l.size(); j++) {
				if (l.get(j) > max) {
					max = l.get(j);
					k = j;
				}
			}
			if (i != k) {
				Collections.swap(list, i, k);
				Collections.swap(l, i, k);
			}
		}
		
		return list;
	}
}
